package com.boil.exception;

import com.boil.pojo.ResultVo;
import com.boil.util.Constants;
import com.boil.util.FastJsonUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Created by ChenYong on 2016-09-21.
 * <br>
 * 全局异常处理机制。
 */
public class GlobalHandlerExceptionResolver implements HandlerExceptionResolver {
    /**
     * 日志
     */
    private static Logger log = LoggerFactory.getLogger(GlobalHandlerExceptionResolver.class);

    /**
     * 处理异常。
     *
     * @param request  请求
     * @param response 响应
     * @param handler  处理机制
     * @param ex       所有异常
     * @return 视图
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 获取登录用户的操作信息
        Subject subject = SecurityUtils.getSubject();
        // 获取 Session
        Session session = subject.getSession();
        // 视图模型
        ModelAndView modelAndView = new ModelAndView();
        // 请求路径
        String servletPath = (StringUtils.isBlank(request.getServletPath()) ? Constants.EMPTY_STR : request.getServletPath());
        // 获取菜单 ID
        long menuId = (Long) session.getAttribute(Constants.MENU_ID);
        // 获取菜单父 ID
        long menuParentId = (Long) session.getAttribute(Constants.MENU_PARENT_ID);

        // 请求参数异常
        if (ex instanceof RequestParamException) {
            RequestParamException ex1 = (RequestParamException) ex;

            // Ajax 请求/JSON 请求
            if (Constants.AJAX_REQUEST.equalsIgnoreCase(request.getHeader(Constants.REQUEST_TYPE)) || servletPath.endsWith(Constants.SUFFIX_JSON)) {
                log.error("Ajax 请求/JSON 请求--->请求参数不合法");

                if (Constants.AJAX_REQUEST.equalsIgnoreCase(request.getHeader(Constants.REQUEST_TYPE))) {
                    // 请求参数不合法：412
                    response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
                }

                modelAndView.setViewName(null);

                PrintWriter pw = null;

                try {
                    pw = response.getWriter();

                    pw.write(FastJsonUtils.bean2Json(ex1.getResultVo()));
                } catch (IOException e) {
                    log.error("Ajax 请求/JSON 请求--->响应请求参数不合法时出现异常，异常信息如下：{}", e);
                } finally {
                    try {
                        if (pw != null) {
                            pw.close();
                        }
                    } catch (Exception e) {
                        log.error("Ajax 请求/JSON 请求--->响应请求参数不合法，关闭响应流时出现异常，异常信息如下：{}", e);
                    }
                }

                // 普通请求
            } else {
                log.error("普通请求--->请求参数不合法");

                SecurityUtils.getSubject().getSession().setAttribute(Constants.ERROR_412_STR, ex1.getResultVo());

                modelAndView.setViewName(String.format("redirect:/web/error/412.html?menuId=%s&menuParentId=%s", menuId, menuParentId));
            }

            // 未授权访问
        } else if (ex instanceof UnauthorizedException) {
            // 结果
            ResultVo resultVo = new ResultVo();
            resultVo.setResultCode(Constants.ERROR_403);
            resultVo.setResultMsg(Constants.ERROR_403_MSG);

            // Ajax 请求/JSON 请求
            if (Constants.AJAX_REQUEST.equalsIgnoreCase(request.getHeader(Constants.REQUEST_TYPE)) || servletPath.endsWith(Constants.SUFFIX_JSON)) {
                log.error("Ajax 请求/JSON 请求--->未授权访问");

                if (Constants.AJAX_REQUEST.equalsIgnoreCase(request.getHeader(Constants.REQUEST_TYPE))) {
                    // 未授权访问：403
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                }

                modelAndView.setViewName(null);

                PrintWriter pw = null;

                try {
                    pw = response.getWriter();

                    pw.write(FastJsonUtils.bean2Json(resultVo));
                } catch (IOException e) {
                    log.error("Ajax 请求/JSON 请求--->响应未授权访问时出现异常，异常信息如下：{}", e);
                } finally {
                    try {
                        if (pw != null) {
                            pw.close();
                        }
                    } catch (Exception e) {
                        log.error("Ajax 请求/JSON 请求--->响应未授权访问，关闭响应流时出现异常，异常信息如下：{}", e);
                    }
                }

                // 非 Ajax 请求
            } else {
                log.error("普通请求--->未授权访问");

                modelAndView.setViewName(String.format("redirect:/web/error/403.html?menuId=%s&menuParentId=%s", menuId, menuParentId));
            }

            // 服务器内部异常
        } else {
            // 结果
            ResultVo resultVo = new ResultVo();
            resultVo.setResult(ExceptionUtils.getStackTrace(ex));
            resultVo.setResultCode(Constants.SYS_ERROR);
            resultVo.setResultMsg(Constants.SYS_ERROR_MSG);

            // Ajax 请求/JSON 请求
            if (Constants.AJAX_REQUEST.equalsIgnoreCase(request.getHeader(Constants.REQUEST_TYPE)) || servletPath.endsWith(Constants.SUFFIX_JSON)) {
                log.error("Ajax 请求/JSON 请求--->服务器内部异常");

                if (Constants.AJAX_REQUEST.equalsIgnoreCase(request.getHeader(Constants.REQUEST_TYPE))) {
                    // 服务器内部异常：500
                    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                }

                modelAndView.setViewName(null);

                PrintWriter pw = null;

                try {
                    pw = response.getWriter();

                    pw.write(FastJsonUtils.bean2Json(resultVo));
                } catch (IOException e) {
                    log.error("Ajax 请求/JSON 请求--->响应服务器内部异常时出现异常，异常信息如下：{}", e);
                } finally {
                    try {
                        if (pw != null) {
                            pw.close();
                        }
                    } catch (Exception e) {
                        log.error("Ajax 请求/JSON 请求--->响应服务器内部异常，关闭响应流时出现异常，异常信息如下：{}", e);
                    }
                }

                // 普通请求
            } else {
                log.error("普通请求--->服务器内部异常");

                SecurityUtils.getSubject().getSession().setAttribute(Constants.ERROR_500_STR, resultVo);

                modelAndView.setViewName(String.format("redirect:/web/error/500.html?menuId=%s&menuParentId=%s", menuId, menuParentId));
            }

            log.error("服务器内部异常：{}", ex);
        }

        return modelAndView;
    }
}